package com.jbm.core.common;

import com.google.protobuf.ByteString;
import com.google.protobuf.GeneratedMessage;
import com.jbm.core.iface.IAcross;
import com.jbm.core.pb.PBIAcross;

import java.lang.reflect.*;
import java.util.*;

/**
 * Created by root on 16-5-6.
 */
public class TransClass
{
	protected HashMap<String,String> m_p2p;
	protected int m_leftver;
	protected int m_rightver;

	public TransClass ()
	{
		m_leftver = m_rightver = 2;
	}

	public TransClass ( int leftver, int rightver )
	{
		this();

		m_leftver = leftver;
		m_rightver = rightver;
	}

	private String getP2P( String name )
	{
		String rtn = m_p2p.get(name);

		if ( rtn == null )
			return name;
		else
			return rtn;
	}

	protected boolean P2I ( FacePBAtt2Map fp2map, Object pb, Object iface )
	{
		FacePBAttMap fpmap = null;
		if ( m_leftver == 1 && m_rightver == 1 )
			fpmap = fp2map.m_ofpm;
		else if ( m_leftver == 2 && m_rightver == 2 )
			fpmap = fp2map.m_fpm;
		else
			return false;

		if ( fpmap.m_ifacefield == null )
			return false;

		if ( fpmap.m_pbislist )
		{
			if ( !fpmap.m_ifaceisarray && !fpmap.m_ifaceislst )
				return false;

			if ( fpmap.m_pbgetlstm == null ||
					fpmap.m_pbgetcntm == null )
				return false;

			Object value = null;
			List<?> listpb = null;

			try
			{
				Method getlistm = fpmap.m_pbgetlstm;
				Method getcountm = fpmap.m_pbgetcntm;

				listpb = (List<?>)getlistm.invoke(pb);
				int count = Integer.parseInt(getcountm.invoke(pb).toString());

				if ( count == 0 )
					return false;

				value = Array.newInstance(fpmap.m_ifacetype, count);

				if ( value == null )
					return false;

				Iterator it = listpb.iterator();
				int i = 0;
				while ( it.hasNext())
				{
					Object li_value = it.next();
					if ( fpmap.m_isclass )
					{
						if ( li_value instanceof ByteString )
							li_value = BStoIA(fpmap.m_ifacetype.getSimpleName(),(ByteString)li_value);
						else
							li_value = ProtocolBufferToI(li_value);
					}else
						li_value = Cast.Cast(li_value,fpmap.m_pbtype);

					Array.set(value,i,li_value);

					i++;
				}
				fpmap.m_ifacefield.setAccessible(true);

				if ( fpmap.m_ifaceislst )
				{
					Object[] avalue = (Object[])value;
					value = Arrays.asList(avalue);
				}

				fpmap.m_ifacefield.set(iface, value);
			}catch ( Exception e)
			{
//				e.printStackTrace();
			}
		}else
		{
			Object value = null;

			if ( fpmap.m_pbgetm == null ||
					fpmap.m_pbhasm == null )
				return false;

			try
			{
				Method getm = fpmap.m_pbgetm;
				Method hasm = fpmap.m_pbhasm;
				if ( ( Boolean)hasm.invoke(pb) )
				{
					value = getm.invoke(pb);

					if ( fpmap.m_isclass )
					{
						if ( value instanceof ByteString )
							value = BStoIA(fpmap.m_ifacetype.getSimpleName(),(ByteString)value);
						else
							value = ProtocolBufferToI(value);
					}else
					{
						Class<?> ctype = fpmap.m_ifacetype;

						if ( fpmap.m_ifaceisarray && fpmap.m_ifacetype == byte.class )
							ctype = ByteString.class;

						value = Cast.Cast(value,ctype);

						if ( fpmap.m_ifaceisarray && fpmap.m_ifacetype == byte.class )
						{
							ByteString bs = ( ByteString )value;
							value = bs.toByteArray();
						}
					}

					fpmap.m_ifacefield.setAccessible(true);
					fpmap.m_ifacefield.set(iface, value);
				}
			}catch ( Exception e)
			{
//				e.printStackTrace();
			}
		}

		return true;
	}

	protected boolean I2P ( FacePBAtt2Map fp2map, Object pbb, Object iface )
	{
		FacePBAttMap facemap = null;
		FacePBAttMap pbbmap = null;

		if ( m_leftver == 1 )
			facemap = fp2map.m_ofpm;
		else
			facemap = fp2map.m_fpm;

		if ( m_rightver == 2 )
			pbbmap = fp2map.m_fpm;
		else
			return false;

		if ( facemap.m_ifacefield == null )
			return false;

		if ( pbbmap.m_pbislist )
		{
			if ( !facemap.m_ifaceisarray && !facemap.m_ifaceislst )
				return false;

			if ( pbbmap.m_pbbaddm == null )
				return false;

			Object value = null;

			try
			{
				facemap.m_ifacefield.setAccessible(true);
				value = facemap.m_ifacefield.get(iface);

				if ( value == null )
					return false;

				if ( facemap.m_ifaceislst )
				{
					Method toarray = value.getClass().getMethod("toArray");
					value = toarray.invoke(value);
				}

				Method addm = pbbmap.m_pbbaddm;

				int count = Array.getLength(value);

				for ( int i = 0; i < count; i ++ )
				{
					Object li_value = Array.get(value,i);

					if ( facemap.m_isclass )
					{
						li_value = ItoProtocolBuffer(li_value);
						if ( pbbmap.m_pbtype == ByteString.class )
							li_value = ((GeneratedMessage) li_value).toByteString();
					}else
						li_value = Cast.Cast(li_value,pbbmap.m_pbtype);

					addm.invoke(pbb,li_value);
				}
			}catch (Exception e)
			{
//				e.printStackTrace();
			}
		}else
		{
			Object value = null;

			if ( pbbmap.m_pbbsetm == null )
				return false;

			try
			{
				facemap.m_ifacefield.setAccessible(true);
				value = facemap.m_ifacefield.get(iface);

				if ( value == null )
					return false;

				Method setm = pbbmap.m_pbbsetm;

				if ( facemap.m_isclass )
				{
					value = ItoProtocolBuffer(value);
					if ( pbbmap.m_pbtype == ByteString.class )
						value = ((GeneratedMessage) value).toByteString();
				}else
				{
					if (facemap.m_ifaceisarray && facemap.m_ifacetype == byte.class)
						value = ByteString.copyFrom((byte[]) value);

					value = Cast.Cast(value, pbbmap.m_pbtype);
				}

				setm.invoke(pbb,value);
			}catch (Exception e)
			{
//				e.printStackTrace();
			}
		}
		return true;
	}

	public Object ProtocolBufferToI (Object pb)
	{
		FaceMap fmap = AutoCall.g_autocall.getM_all_classes().get(pb.getClass().getSimpleName());

		if ( fmap == null )
			return null;

		Class<?> demo = null;

		if ( m_leftver == 1 && m_rightver == 1 )
			demo = fmap.m_oifaceclazz;
		else if ( m_leftver == 2 && m_rightver == 2 )
			demo = fmap.m_ifaceclazz;
		else
			return null;

		Object iface = null;

		try
		{
			iface = demo.newInstance();
		}catch (Exception e)
		{
//			e.printStackTrace();
		}

		if ( iface == null )
			return null;

		Iterator iter = fmap.m_fp2map.entrySet().iterator();

		while(iter.hasNext())
		{
			Map.Entry entry = (Map.Entry)iter.next();

			P2I( (FacePBAtt2Map)entry.getValue(), pb, iface );
		}

		return iface;
	}

	public Object ItoProtocolBuffer ( Object iface )
	{
		Object pbb = null;

		Class<?> ifaceclass = iface.getClass();

		FaceMap fmap = null;

		if ( m_rightver != 2 )
			return false;

		do
		{
			fmap = AutoCall.g_autocall.getM_all_classes().get(ifaceclass.getSimpleName());

			if ( fmap != null )
				try
				{
					pbb = fmap.m_newbuilder.invoke(null);
				} catch (Exception e)
				{
//					e.printStackTrace();
				}
			else
				ifaceclass = ifaceclass.getSuperclass();

		}while ( fmap == null && ifaceclass != null && !ifaceclass.getSimpleName().equals("IAcross"));

		if (pbb == null)
			return null;

		Iterator iter = fmap.m_fp2map.entrySet().iterator();

		while(iter.hasNext())
		{
			Map.Entry entry = (Map.Entry)iter.next();

			I2P( (FacePBAtt2Map)entry.getValue(),pbb,iface);
		}

		Object pb = null;

		try
		{
			pb = fmap.m_pbbuild.invoke(pbb);
		}catch (Exception e)
		{
//			e.printStackTrace();
		}

		return pb;
	}

	public PBIAcross.IAcross I2_0toPB2_0(String errorheader, IAcross i2_0)
	{
		m_leftver = m_rightver = 2;
		return I2_0toPB2_0Builder(errorheader, i2_0).build();
	}

	public PBIAcross.IAcross.Builder I2_0toPB2_0Builder(String errorheader, IAcross i2_0)
	{
		m_leftver = m_rightver = 2;
		PBIAcross.IAcross.Builder builder = PBIAcross.IAcross.newBuilder();


		if ( i2_0.m_code != 0 )
			builder.setCode(i2_0.m_code);
		if ( i2_0.m_info != null )
			builder.setInfo(i2_0.m_info);
//		if ( i2_0.m_ca_key != null )
//			builder.setCaKey(i2_0.m_ca_key);
		if ( i2_0.m_query != null )
			builder.setQuery(i2_0.m_query);
		if ( i2_0.m_fromid != 0 )
			builder.setFromid(i2_0.m_fromid);
		if ( i2_0.m_toid != 0 )
			builder.setToid(i2_0.m_toid);
		if ( i2_0.m_fromtag != null )
			builder.setFromtag(i2_0.m_fromtag);
		if ( i2_0.m_totag != null )
			builder.setTotag(i2_0.m_totag);
		if ( i2_0.m_randr != 0 )
			builder.setRandr(i2_0.m_randr);
		if ( i2_0.m_qindex >= 0 )
			builder.setQindex(i2_0.m_qindex);
		if ( i2_0.m_timestamp != 0l )
			builder.setTimestamp(i2_0.m_timestamp);

		builder.setExcode(i2_0.m_excode);

		if ( !i2_0.getClass().getSimpleName().equals("IAcross") )
		{
			builder.setClass_(i2_0.getClass().getSimpleName());

			GeneratedMessage pb = null;

			pb = (GeneratedMessage) new TransClass(2,2).ItoProtocolBuffer(i2_0);

			if ( pb != null )
			{
				builder.setContent(pb.toByteString());
			}else
			{
				builder.setCode(500);
				builder.setInfo(errorheader+i2_0.getClass().getName()+" I2P error");
			}
		}

		return builder;
	}

	public IAcross PB2_0toI2_0(String errorheader, PBIAcross.IAcross pb2_0)
	{
		m_leftver = m_rightver = 2;
		int code = 0;
		String info = null;
		IAcross i2_0 = null;

		if (pb2_0.hasContent())
		{
			FaceMap fmap = AutoCall.g_autocall.getM_all_classes().get(pb2_0.getClass_());
			Method parsefrom = null;

			if ( fmap != null && fmap.m_pbclazz != null )
			{
				try
				{
					parsefrom = fmap.m_parsefrom;
				}catch (Exception e)
				{
					code = 405;
					info = fmap.m_pbclassname+".parseFrom(byte[]) not found";
				}
			}else
			{
				code = 404;
				info = "PB" + pb2_0.getClass_() + " not found";
			}

			if ( parsefrom != null )
			{
				GeneratedMessage pbcontent = null;
				try
				{
					pbcontent = (GeneratedMessage) parsefrom.invoke(null, pb2_0.getContent());
				}catch (Exception e)
				{
					code = 500;
					info = fmap.m_pbclassname+".parseFrom(ByteString) invoke error";
				}

				if ( pbcontent != null )
				{
					i2_0 = (IAcross)new TransClass(2,2).ProtocolBufferToI(pbcontent);

					if (i2_0 == null)
					{
						code = 500;
						info = pbcontent.getClass().getName()+" P2I error";
					}
				}
			}
		}

		if ( i2_0 == null )
			i2_0 = new IAcross();

		if ( code != 0 )
			i2_0.m_code = code;
		else if (pb2_0.hasCode())
			i2_0.m_code = pb2_0.getCode();

		if ( info != null )
			i2_0.m_info = errorheader+info;
		else if (pb2_0.hasInfo())
			i2_0.m_info = pb2_0.getInfo();

//		if (pb2_0.hasCaKey())
//			i2_0.m_ca_key = pb2_0.getCaKey();
		if (pb2_0.hasQuery())
			i2_0.m_query = pb2_0.getQuery();
		if (pb2_0.hasFromid())
			i2_0.m_fromid = pb2_0.getFromid();
		if (pb2_0.hasToid())
			i2_0.m_toid = pb2_0.getToid();
		if (pb2_0.hasFromtag())
			i2_0.m_fromtag = pb2_0.getFromtag();
		if (pb2_0.hasTotag())
			i2_0.m_totag = pb2_0.getTotag();
		if (pb2_0.hasRandr())
			i2_0.m_randr = pb2_0.getRandr();
		if (pb2_0.hasQindex())
			i2_0.m_qindex = pb2_0.getQindex();
		if (pb2_0.hasExcode())
			i2_0.m_excode = pb2_0.getExcode();
		if (pb2_0.hasTimestamp())
			i2_0.m_timestamp = pb2_0.getTimestamp();

		return i2_0;
	}

	public Object BStoPB(String classname, ByteString bs2_0)
	{
		FaceMap fmap = AutoCall.g_autocall.getM_all_classes().get(classname);

		if (fmap == null)
			return null;

		Method parsefrom = null;


		if ( m_rightver == 1 )
			parsefrom = fmap.m_oparsefrom;
		else if ( m_rightver == 2 )
			parsefrom = fmap.m_parsefrom;

		if ( parsefrom == null )
			return null;

		GeneratedMessage pbcontent = null;
		try
		{
			pbcontent = (GeneratedMessage) parsefrom.invoke(null, bs2_0);
		}catch (Exception e)
		{
			return null;
		}

		return pbcontent;
	}

	public IAcross BStoIA(String classname, ByteString bs2_0 )
	{
		Object pbcontent = BStoPB(classname,bs2_0);

		if ( pbcontent != null )
			return (IAcross)ProtocolBufferToI(pbcontent);
		else
			return null;
	}

	protected boolean P2P ( FacePBAttMap fpbb, FacePBAttMap fpb, Object pbb, Object pb )
	{
		if ( fpb.m_pbislist != fpbb.m_pbislist || fpb.m_isclass != fpbb.m_isclass )
			return false;

		if ( fpb.m_pbislist )
		{
			Object value = null;
			List<?> listpb = null;

			if ( fpbb.m_pbbaddm == null ||
					fpb.m_pbgetlstm == null ||
					fpb.m_pbgetcntm == null )
				return false;

			try
			{
				Method addm = fpbb.m_pbbaddm;
				HashMap<String,Field> fields = null;

				Method getlistm = fpb.m_pbgetlstm;
				Method getcountm = fpb.m_pbgetcntm;

				listpb = (List<?>)getlistm.invoke(pb);

				Iterator it = listpb.iterator();

				while ( it.hasNext())
				{
					Object li_value = it.next();
					if ( fpb.m_isclass )
					{
						if ( fpb.m_pbtype == ByteString.class )
						{
							if ( fpbb.m_pbtype != ByteString.class)
								li_value = BStoPB(fpbb.m_pbtype.getSimpleName(), (ByteString)li_value);
						}else
						{
							li_value = PBtoPB(fpbb.m_pbtype.getSimpleName(), li_value);
							if ( fpbb.m_pbtype == ByteString.class)
								li_value = ((GeneratedMessage) li_value).toByteString();
						}
					}else
						li_value = Cast.Cast(li_value,fpbb.m_pbtype);

					addm.invoke(pbb,li_value);
				}
			}catch ( Exception e)
			{
//				e.printStackTrace();
			}
		}else
		{

			if ( fpbb.m_pbbsetm == null ||
					fpb.m_pbgetm == null ||
					fpb.m_pbhasm == null )
				return false;

			Method setm = fpbb.m_pbbsetm;

			Object value = null;

			try
			{
				Method getm = fpb.m_pbgetm;
				Method hasm = fpb.m_pbhasm;

				if ( ( Boolean)hasm.invoke(pb) )
				{
					value = getm.invoke(pb);

					if ( fpb.m_isclass )
					{
						if ( fpb.m_pbtype == ByteString.class )
						{
							if (fpbb.m_pbtype != ByteString.class)
								value = BStoPB(fpbb.m_pbtype.getSimpleName(), (ByteString)value);
						}else
						{
							value = PBtoPB(fpbb.m_pbtype.getSimpleName(), value);
							if (fpbb.m_pbtype == ByteString.class)
								value = ((GeneratedMessage) value).toByteString();
						}
					}else
						value = Cast.Cast(value, fpbb.m_pbtype);

					setm.invoke(pbb,value);
				}
			}catch ( Exception e)
			{
//				e.printStackTrace();
			}
		}
		return true;
	}

	protected Object PBtoPBBuilder(String classname, Object pb)
	{
		Object pbb = null;

		String pbname = null;

		if ( classname == null )
			pbname = getP2P(pb.getClass().getSimpleName());
		else
			pbname = classname;

		FaceMap fpbmap = AutoCall.g_autocall.getM_all_classes().get(pb.getClass().getSimpleName());
		FaceMap fpbbmap = AutoCall.g_autocall.getM_all_classes().get(pbname);

		if ( fpbmap == null || fpbbmap == null )
			return null;

		int leftv = m_leftver;
		int rightv = m_rightver;

		try
		{
			if (m_rightver == 1)
			{
				if (fpbbmap.m_onewbuilder != null)
					pbb = fpbbmap.m_onewbuilder.invoke(null);
				else if (fpbbmap.m_newbuilder != null)
				{
					pbb = fpbbmap.m_newbuilder.invoke(null);
					rightv = 2;
				}
			}else if (fpbbmap.m_newbuilder != null)
				pbb = fpbbmap.m_newbuilder.invoke(null);
		}catch (Exception e)
		{
		}

		if (pbb == null)
			return null;

		Iterator iter = fpbmap.m_fp2map.entrySet().iterator();

		while(iter.hasNext())
		{
			Map.Entry entry = (Map.Entry)iter.next();

			FacePBAtt2Map fpb2map = (FacePBAtt2Map)entry.getValue();
			FacePBAtt2Map fpbb2map = fpbbmap.m_fp2map.get(entry.getKey());

			if ( fpbb2map == null )
				continue;

			FacePBAttMap fpb = null;
			FacePBAttMap fpbb = null;

			if ( leftv == 1 )
				fpb = fpb2map.m_ofpm;
			else
				fpb = fpb2map.m_fpm;

			if ( rightv == 1 )
				fpbb = fpbb2map.m_ofpm;
			else
				fpbb = fpbb2map.m_fpm;

			P2P( fpbb, fpb, pbb,pb);
		}

		return pbb;
	}

	protected Object PBtoPB (String classname, Object pb)
	{
		Object pbb = PBtoPBBuilder(classname,pb);

		if ( pbb == null )
			return null;

		Object rpb = null;

		try
		{
			Method build = pbb.getClass().getMethod("build");
			rpb = build.invoke(pbb);
		}catch (Exception e)
		{
//			e.printStackTrace();
		}

		return rpb;
	}

	public PBIAcross.IAcross I1_0toPB2_0(Object iface)
	{
		m_leftver = 1;
		m_rightver = 2;

		GeneratedMessage pb1_0 = (GeneratedMessage)
				new TransClass(1,2)
				.ItoProtocolBuffer(iface);
		return PB1_0toPB2_0(pb1_0,2);
	}

	public PBIAcross.IAcross PB1_0toPB2_0(Object pb1_0,int leftv)
	{
		m_leftver = leftv;
		m_rightver = 2;

		m_p2p = OHCodeFactory.t10to20;

		String pb2_0clsname = getP2P(pb1_0.getClass().getSimpleName());

		FaceMap fpbbmap = AutoCall.g_autocall.getM_all_classes().get(pb2_0clsname);

		if ( fpbbmap == null )
			return null;

		Method nb = fpbbmap.m_newbuilder;

		if (nb == null)
			return null;

		try
		{
			GeneratedMessage pb = (GeneratedMessage) PBtoPB(pb2_0clsname, pb1_0);

			PBIAcross.IAcross.Builder builder = PBIAcross.IAcross.newBuilder();

			builder.setRandr(RandR.RANDR_TYPE_REQUEST);

			builder.setClass_(pb2_0clsname);
			builder.setMethod((String) pb1_0.getClass().getMethod("getMethod").invoke(pb1_0));

			if (pb != null)
				builder.setContent(pb.toByteString());
			else
			{
				builder.setCode(500);
				builder.setInfo(pb1_0.getClass().getName() + " PBtoPB error");
			}

			return builder.build();
		}catch (Exception e)
		{
			return null;
		}
	}

	public Object PB2_0toI1_0(PBIAcross.IAcross pb2_0)
	{
		m_leftver = 2;
		m_rightver = 1;

		GeneratedMessage pb1_0 = (GeneratedMessage)PB2_0toPB1_0(pb2_0);
		return new TransClass(1,1).ProtocolBufferToI(pb1_0);
	}

	public Object PB2_0toPB1_0(PBIAcross.IAcross pb2_0)
	{
		m_leftver = 2;
		m_rightver = 1;

		m_p2p = OHCodeFactory.t20to10;

		String pb1_0clsname = getP2P(pb2_0.getClass_());

		FaceMap f20map = AutoCall.g_autocall.getM_all_classes().get(pb2_0.getClass_());

		if ( f20map == null )
			return null;

		GeneratedMessage pb = null;
		Method parsefrom = f20map.m_parsefrom;

		try
		{
			if ( parsefrom == null )
				return null;

			pb = (GeneratedMessage) parsefrom.invoke(null, pb2_0.getContent());
			if ( pb == null )
				return null;


			Object pbb = PBtoPBBuilder(pb1_0clsname, pb);

			Method method = null;

			boolean back = pb2_0.getCode()==200?true:false;
			String ei = pb2_0.getInfo();

			try
			{

				pbb.getClass().getMethod("setBack",boolean.class).invoke(pbb,back);
			}catch (Exception e)
			{
			}
			try
			{

				pbb.getClass().getMethod("setErrorinfo", String.class).invoke(pbb,ei);
			}catch (Exception e)
			{
			}

			Object rpb = null;

			try
			{
				Method build = pbb.getClass().getMethod("build");
				rpb = build.invoke(pbb);
			}catch (Exception e)
			{
//			e.printStackTrace();
			}

			return rpb;
		}catch (Exception e)
		{
			return null;
		}
	}
}
